package cn.bearspark.framework.redis.starter.buffer.hash;

import cn.bearspark.framework.redis.starter.buffer.RedisBuffer;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * Redis 的 hash 数据结构的缓存器的抽象基类
 *
 * @param <CO> 缓存对象的类型
 * @author f
 */
public abstract class RedisBaseHashBuffer<CO> extends RedisBuffer {

    /**
     * 缓存的过期时间，单位：s
     */
    protected final int cacheExpire;

    /**
     * 缓存对象的 Class 对象
     */
    protected final Class<CO> coClazz;

    /**
     * 热数据字段的 getter，至少有一个热数据字段
     */
    protected final Method[] hotFieldGetters;

    /**
     * 热数据字段的 setter，至少有一个热数据字段
     */
    protected final Method[] hotFieldSetters;

    /**
     * 热数据的字段名数组，至少有一个热数据字段名
     */
    protected final Object[] hotFieldsName;

    public RedisBaseHashBuffer(RedisTemplate<String, Object> redisTemplate,
                               int cacheExpire,
                               Class<CO> coClazz) {
        super(redisTemplate);
        this.cacheExpire = cacheExpire;
        try {
            this.coClazz = coClazz;

            Field[] hotFields = getHotFields(coClazz.getDeclaredFields());
            int length = hotFields.length;
            this.hotFieldGetters = new Method[length];
            this.hotFieldSetters = new Method[length];
            this.hotFieldsName = new Object[length];
            for (int i = 0; i < length; i++) {
                Field hotField = hotFields[i];
                String fieldName = hotField.getName();
                hotFieldGetters[i] = coClazz.getMethod(getGetterName(fieldName));
                hotFieldSetters[i] = coClazz.getMethod(getSetterName(fieldName), hotField.getType());
                hotFieldsName[i] = fieldName;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // 根据字段名称获取 setter 名称
    private String getSetterName(String fieldName) {
        return "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    // 根据字段名称获取 getter 名称
    private String getGetterName(String fieldName) {
        return "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    }

    // 在指定的字段数组中获取热数据字段数组
    private Field[] getHotFields(Field[] fields) {
        List<Field> hotFieldsList = new ArrayList<>();
        for (Field field : fields) {
            if (field.isAnnotationPresent(HotData.class)) {
                hotFieldsList.add(field);
            }
        }
        return hotFieldsList.toArray(new Field[0]);
    }

    /**
     * 从 Redis 中获取的数据映射中生成一个新的对象
     *
     * @param entries Redis 中获取的数据映射
     * @return 新的对象
     */
    protected CO fromEntries(Map<Object, Object> entries) {
        return BeanUtil.mapToBean(entries, coClazz, false, CopyOptions.create());
    }

    /**
     * 获取指定数据中指定条目的值
     *
     * @param key            待获取数据的键
     * @param entryNameList  条目的字段名称
     * @param hashOperations Redis 中的 hash 操作
     * @return 指定数据中指定条目的值组成的映射，key：条目的字段名称，value：条目的值
     */
    protected Map<Object, Object> getEntries(String key, List<String> entryNameList,
                                             HashOperations<String, Object, Object> hashOperations) {
        Map<Object, Object> entries = new HashMap<>(entryNameList.size());
        entryNameList.forEach(fieldName -> entries.put(fieldName,
                hashOperations.get(key, fieldName)
        ));
        return entries;
    }
}
